
package com.csc440.team2.smil;

import java.io.IOException;
import java.io.StringReader;


import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;


import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;


import com.csc440.team2.smil.layout.SMILRegion;


/**
 * Reader class to parse SMIL Message XML documents
 * 
 * @author Brad Gardner, Alex Povkov
 */
public class SMILReader extends DefaultHandler
{
    private SMILMessage currentMessage;
    private SMILComponent currentComponent;
    private boolean readyForText;
    private SMILRegion currentRegion;


    public SMILReader()
    {
        super();
    }


    /**
     * Parses the input string as XML and creates a SMILMessage object
     * 
     * @param input XML document string
     * @return SMILMessage created from XML
     * @throws SMILReadException
     */
    public static SMILMessage parseMessage(String input) throws SMILReadException
    {
        try
        {
            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser sp = spf.newSAXParser();
            XMLReader xr = sp.getXMLReader();
            SMILReader parser = new SMILReader();
            xr.setContentHandler(parser);
            xr.setErrorHandler(parser);

            xr.parse(new InputSource(new StringReader(input)));

            SMILMessage message = parser.getMessage();

            return message;
        }
        catch (Exception e)
        {
            if (e instanceof SAXException)
                throw new SMILReadException("SAXException occured while parsing the XML message.", e);
            else if (e instanceof IOException)
                throw new SMILReadException("IOException occured while parsing the XML message.", e);
            else if (e instanceof ParserConfigurationException)
                throw new SMILReadException("ParserConfigurationException occured while parsing the XML message.", e);
            else
                throw new SMILReadException("An error occured while parsing the XML message.", e);
        }
    }


    @Override
    /**
     * Called at the start of each element, parses the element type and builds a corresponding component for it.
     */
    public void startElement(String uri, String name, String qName, Attributes atts)
    {
        readyForText = false;

        if ("root-layout".equalsIgnoreCase(name))
        {
            int width = Integer.parseInt(atts.getValue("width"));
            int height = Integer.parseInt(atts.getValue("height"));

            currentMessage = SMILMessage.create(width, height);
            currentRegion = currentMessage.getLayoutRoot();
        }
        else if ("region".equalsIgnoreCase(name))
        {
            SMILRegion region = new SMILRegion();

            int width = Integer.parseInt(atts.getValue("width"));
            int height = Integer.parseInt(atts.getValue("height"));
            int left = Integer.parseInt(atts.getValue("left"));
            int top = Integer.parseInt(atts.getValue("top"));
            String regionId = atts.getValue("id");

            region.setWidth(width);
            region.setHeight(height);
            region.setX(left);
            region.setY(top);
            region.setId(regionId);

            currentRegion.addChild(region);

            currentRegion = region;
        }
        else if ("text".equalsIgnoreCase(name))
        {
            SMILTextComponent component = SMILTextComponent.create();

            if (atts.getValue("begin") == null)
                component.setBegin(0);
            else
                component.setBegin(Integer.parseInt(atts.getValue("begin")));
            component.setDuration(Integer.parseInt(atts.getValue("dur")));
            component.setRegionId(atts.getValue("region"));

            currentComponent = component;

            currentMessage.addComponent(currentComponent);
            readyForText = true;
        }
        else if ("audio".equalsIgnoreCase(name))
        {
            SMILAudioComponent component = SMILAudioComponent.create();

            if (atts.getValue("begin") == null)
                component.setBegin(0);
            else
                component.setBegin(Integer.parseInt(atts.getValue("begin")));

            component.setDuration(Integer.parseInt(atts.getValue("dur")));
            component.setSrc(atts.getValue("src"));
            component.setRegionId(atts.getValue("region"));

            currentComponent = component;

            currentMessage.addComponent(currentComponent);
        }
        else if ("img".equalsIgnoreCase(name))
        {
            SMILImageComponent component = SMILImageComponent.create();

            if (atts.getValue("begin") == null)
                component.setBegin(0);
            else
                component.setBegin(Integer.parseInt(atts.getValue("begin")));

            component.setDuration(Integer.parseInt(atts.getValue("dur")));
            component.setSrc(atts.getValue("src"));
            component.setRegionId(atts.getValue("region"));

            currentComponent = component;

            currentMessage.addComponent(currentComponent);
        }
        else if ("video".equalsIgnoreCase(name))
        {
            SMILVideoComponent component = SMILVideoComponent.create();

            if (atts.getValue("begin") == null)
                component.setBegin(0);
            else
                component.setBegin(Integer.parseInt(atts.getValue("begin")));

            component.setDuration(Integer.parseInt(atts.getValue("dur")));
            component.setSrc(atts.getValue("src"));
            component.setRegionId(atts.getValue("region"));

            currentComponent = component;

            currentMessage.addComponent(currentComponent);
        }
    }


    /*
     * (non-Javadoc)
     * 
     * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String,
     * java.lang.String, java.lang.String)
     */
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException
    {
        if ("region".equalsIgnoreCase(localName))
        {
            currentRegion = currentRegion.getParent();
        }
    }


    @Override
    public void characters(char ch[], int start, int length)
    {
        // only build buffers and read the characters if we have to
        if (readyForText && currentComponent instanceof SMILTextComponent)
        {
            StringBuffer buffer = new StringBuffer();

            for (int i = start; i < start + length; i++)
                buffer.append(ch[i]);

            readyForText = false;
            SMILTextComponent textComponent = (SMILTextComponent) currentComponent;
            textComponent.setText(buffer.toString());
        }

    }


    public SMILMessage getMessage()
    {
        return currentMessage;
    }

}
